using JGSY.CMS.LowCode.Platform.Application.Interfaces;
using JGSY.CMS.LowCode.Platform.Application.DTOs;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace JGSY.CMS.LowCode.Platform.Infrastructure.Services
{
    /// <summary>
    /// 风险评估服务实现
    /// 提供用户行为风险评估的业务逻辑实现，包括设备信任度、位置一致性、时间模式等多维度风险分析
    /// </summary>
    public class RiskAssessmentService : IRiskAssessmentAppService
    {
        /// <summary>
        /// 用户设备仓储接口
        /// </summary>
        private readonly Domain.Interfaces.IUserDeviceRepository _deviceRepo;
        
        /// <summary>
        /// 登录历史仓储接口
        /// </summary>
        private readonly Domain.Interfaces.ILoginHistoryRepository _loginRepo;

        /// <summary>
        /// 初始化风险评估服务实例
        /// </summary>
        /// <param name="deviceRepo">用户设备仓储接口实例</param>
        /// <param name="loginRepo">登录历史仓储接口实例</param>
        public RiskAssessmentService(Domain.Interfaces.IUserDeviceRepository deviceRepo, Domain.Interfaces.ILoginHistoryRepository loginRepo)
        {
            _deviceRepo = deviceRepo;
            _loginRepo = loginRepo;
        }

        /// <summary>
        /// 计算风险评估得分
        /// 基于多个风险因子计算综合风险评估结果，包括设备信任度、位置一致性、时间模式等
        /// </summary>
        /// <param name="context">风险评估上下文信息</param>
        /// <returns>风险评估结果DTO，包含得分、等级、因子分析和建议</returns>
        public async Task<RiskAssessmentResultDto> CalculateRiskScoreAsync(RiskAssessmentContextDto context)
        {
            var factors = await AnalyzeRiskFactorsAsync(context);
            var score =
                factors.DeviceTrust * 0.25 +
                factors.LocationConsistency * 0.2 +
                factors.TimePattern * 0.15 +
                factors.LoginFrequency * 0.2 +
                factors.BehaviorPattern * 0.2;
            var level = GetRiskLevel(score);
            var recommendations = GenerateRecommendations(factors, level);
            return new RiskAssessmentResultDto
            {
                Score = Math.Round(score, 2),
                Level = level,
                Factors = factors,
                Recommendations = recommendations
            };
        }

        /// <summary>
        /// 分析风险因子
        /// 通过多维度分析计算各项风险因子的得分
        /// </summary>
        /// <param name="context">风险评估上下文信息</param>
        /// <returns>风险因子分析结果</returns>
        private async Task<RiskFactorsDto> AnalyzeRiskFactorsAsync(RiskAssessmentContextDto context)
        {
            var deviceTrust = await AnalyzeDeviceTrustAsync(context);
            var locationConsistency = await AnalyzeLocationConsistencyAsync(context);
            var timePattern = await AnalyzeTimePatternAsync(context);
            var loginFrequency = await AnalyzeLoginFrequencyAsync(context);
            var behaviorPattern = await AnalyzeBehaviorPatternAsync(context);
            return new RiskFactorsDto
            {
                DeviceTrust = deviceTrust,
                LocationConsistency = locationConsistency,
                TimePattern = timePattern,
                LoginFrequency = loginFrequency,
                BehaviorPattern = behaviorPattern
            };
        }

        /// <summary>
        /// 分析设备信任度
        /// 基于设备是否为可信设备、设备相似性等因素计算信任度得分
        /// </summary>
        /// <param name="context">风险评估上下文信息</param>
        /// <returns>设备信任度得分（0.0-1.0）</returns>
        private async Task<double> AnalyzeDeviceTrustAsync(RiskAssessmentContextDto context)
        {
            var devices = await _deviceRepo.GetAllAsync();
            var trusted = devices.FirstOrDefault(d => d.UserId == context.UserId && d.Id.ToString() == context.DeviceFingerprint && d.IsTrusted);
            if (trusted != null) return 1.0;
            var userDevices = devices.Where(d => d.UserId == context.UserId).ToList();
            if (userDevices.Count == 0) return 0.1;
            var similar = userDevices.Where(d => CalculateDeviceSimilarity(d.DeviceName, context.UserAgent) > 0.8).ToList();
            if (similar.Count > 0) return 0.7;
            return 0.3;
        }

        /// <summary>
        /// 分析位置一致性
        /// 基于历史登录位置信息计算位置行为的一致性得分
        /// </summary>
        /// <param name="context">风险评估上下文信息</param>
        /// <returns>位置一致性得分（0.0-1.0）</returns>
        private async Task<double> AnalyzeLocationConsistencyAsync(RiskAssessmentContextDto context)
        {
            var logins = (await _loginRepo.GetAllAsync()).Where(l => l.UserId == context.UserId).OrderByDescending(l => l.CreatedAt).Take(20).ToList();
            if (context.Location == null || logins.Count == 0) return 0.5;
            var consistent = logins.Count(l =>
            {
                // 假设 Location 字段为 JSON 字符串，实际可扩展
                var loc = context.Location;
                return loc != null && CalculateDistance(loc.Latitude, loc.Longitude, loc.Latitude, loc.Longitude) < 100;
            });
            var consistency = (double)consistent / logins.Count;
            return consistency;
        }

        /// <summary>
        /// 分析时间模式
        /// 基于历史登录时间分布分析当前登录时间的异常程度
        /// </summary>
        /// <param name="context">风险评估上下文信息</param>
        /// <returns>时间模式得分（0.0-1.0）</returns>
        private async Task<double> AnalyzeTimePatternAsync(RiskAssessmentContextDto context)
        {
            var logins = (await _loginRepo.GetAllAsync()).Where(l => l.UserId == context.UserId).ToList();
            if (logins.Count < 5) return 0.5;
            var currentHour = context.Timestamp.Hour;
            var hourCounts = new int[24];
            foreach (var login in logins) hourCounts[login.CreatedAt.Hour]++;
            var freq = (double)hourCounts[currentHour] / logins.Count;
            if (freq > 0.1) return 1.0 - freq;
            if (currentHour >= 2 && currentHour <= 5) return 0.2;
            return 0.6;
        }

        /// <summary>
        /// 分析登录频率
        /// 基于短期内登录次数判断是否存在异常登录行为
        /// </summary>
        /// <param name="context">风险评估上下文信息</param>
        /// <returns>登录频率得分（0.0-1.0）</returns>
        private async Task<double> AnalyzeLoginFrequencyAsync(RiskAssessmentContextDto context)
        {
            var logins = (await _loginRepo.GetAllAsync()).Where(l => l.UserId == context.UserId).ToList();
            var now = DateTime.UtcNow;
            var hourly = logins.Count(l => (now - l.CreatedAt).TotalHours <= 1);
            var daily = logins.Count(l => (now - l.CreatedAt).TotalDays <= 1);
            if (hourly > 10) return 0.1;
            if (daily > 50) return 0.2;
            if (hourly <= 3 && daily <= 20) return 0.9;
            return 0.6;
        }

        /// <summary>
        /// 分析行为模式
        /// 基于UserAgent和IP地址的一致性分析用户行为模式
        /// </summary>
        /// <param name="context">风险评估上下文信息</param>
        /// <returns>行为模式得分（0.0-1.0）</returns>
        private async Task<double> AnalyzeBehaviorPatternAsync(RiskAssessmentContextDto context)
        {
            var logins = (await _loginRepo.GetAllAsync()).Where(l => l.UserId == context.UserId).Take(50).ToList();
            if (logins.Count == 0) return 0.5;
            var userAgents = logins.Select(l => l.UserAgent).ToList();
            var userAgentConsistency = CalculateUserAgentConsistency(userAgents, context.UserAgent);
            var ips = logins.Select(l => l.IpAddress).ToList();
            var ipConsistency = CalculateIPConsistency(ips, context.IpAddress);
            return (userAgentConsistency + ipConsistency) / 2;
        }

        /// <summary>
        /// 计算设备相似性
        /// 通过比较设备名称和UserAgent中的共同特征计算相似度
        /// </summary>
        /// <param name="deviceName">设备名称</param>
        /// <param name="userAgent">用户代理字符串</param>
        /// <returns>相似度得分（0.0-1.0）</returns>
        private double CalculateDeviceSimilarity(string deviceName, string userAgent)
        {
            if (string.IsNullOrEmpty(deviceName) || string.IsNullOrEmpty(userAgent)) return 0;
            var common = deviceName.Split(' ').Count(part => userAgent.Contains(part) && part.Length > 3);
            var total = Math.Max(deviceName.Split(' ').Length, userAgent.Split(' ').Length);
            return total > 0 ? (double)common / total : 0;
        }

        /// <summary>
        /// 计算两点间距离
        /// 使用Haversine公式计算地球表面两点间的距离（公里）
        /// </summary>
        /// <param name="lat1">第一个点的纬度</param>
        /// <param name="lon1">第一个点的经度</param>
        /// <param name="lat2">第二个点的纬度</param>
        /// <param name="lon2">第二个点的经度</param>
        /// <returns>两点间距离（公里）</returns>
        private double CalculateDistance(double lat1, double lon1, double lat2, double lon2)
        {
            var R = 6371.0;
            var dLat = Deg2Rad(lat2 - lat1);
            var dLon = Deg2Rad(lon2 - lon1);
            var a = Math.Sin(dLat/2) * Math.Sin(dLat/2) + Math.Cos(Deg2Rad(lat1)) * Math.Cos(Deg2Rad(lat2)) * Math.Sin(dLon/2) * Math.Sin(dLon/2);
            var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1-a));
            return R * c;
        }

        /// <summary>
        /// 角度转弧度
        /// 将角度值转换为弧度值，用于地理距离计算
        /// </summary>
        /// <param name="deg">角度值</param>
        /// <returns>弧度值</returns>
        private double Deg2Rad(double deg) => deg * (Math.PI/180);

        /// <summary>
        /// 计算UserAgent一致性
        /// 比较当前UserAgent与历史UserAgent的相似性
        /// </summary>
        /// <param name="historicalUserAgents">历史UserAgent列表</param>
        /// <param name="currentUserAgent">当前UserAgent</param>
        /// <returns>一致性得分（0.0-1.0）</returns>
        private double CalculateUserAgentConsistency(List<string> historicalUserAgents, string currentUserAgent)
        {
            if (historicalUserAgents.Count == 0) return 0.5;
            var similarities = historicalUserAgents.Select(ua => CalculateDeviceSimilarity(ua, currentUserAgent)).ToList();
            return similarities.Max();
        }

        /// <summary>
        /// 计算IP地址一致性
        /// 比较当前IP地址与历史IP地址的一致性，考虑完全匹配、子网匹配和网络段匹配
        /// </summary>
        /// <param name="historicalIPs">历史IP地址列表</param>
        /// <param name="currentIP">当前IP地址</param>
        /// <returns>一致性得分（0.0-1.0）</returns>
        private double CalculateIPConsistency(List<string> historicalIPs, string currentIP)
        {
            if (historicalIPs.Count == 0) return 0.5;
            if (historicalIPs.Contains(currentIP)) return 1.0;
            var currentIPParts = currentIP.Split('.');
            var sameSubnet = historicalIPs.Any(ip => {
                var parts = ip.Split('.');
                return parts.Length >= 3 && currentIPParts.Length >= 3 && parts[0] == currentIPParts[0] && parts[1] == currentIPParts[1] && parts[2] == currentIPParts[2];
            });
            if (sameSubnet) return 0.8;
            var sameNetwork = historicalIPs.Any(ip => {
                var parts = ip.Split('.');
                return parts.Length >= 2 && currentIPParts.Length >= 2 && parts[0] == currentIPParts[0] && parts[1] == currentIPParts[1];
            });
            if (sameNetwork) return 0.6;
            return 0.2;
        }

        private string GetRiskLevel(double score)
        {
            if (score <= 0.3) return "LOW";
            if (score <= 0.6) return "MEDIUM";
            if (score <= 0.8) return "HIGH";
            return "CRITICAL";
        }

        /// <summary>
        /// 根据风险因子和风险等级生成安全建议列表。
        /// </summary>
        /// <param name="factors">风险因子数据对象。</param>
        /// <param name="level">风险等级（如CRITICAL、HIGH、MEDIUM、LOW）。</param>
        /// <returns>安全建议字符串列表。</returns>
        private List<string> GenerateRecommendations(RiskFactorsDto factors, string level)
        {
            var list = new List<string>();
            if (level == "CRITICAL" || level == "HIGH")
            {
                list.Add("建议立即启用两步验证");
                list.Add("建议人工审核此次登录");
            }
            if (factors.DeviceTrust < 0.3) list.Add("检测到新设备，建议验证设备信息");
            if (factors.LocationConsistency < 0.3) list.Add("检测到异常登录位置，建议确认身份");
            if (factors.LoginFrequency < 0.3) list.Add("检测到异常登录频率，建议检查账户安全");
            if (factors.TimePattern < 0.3) list.Add("检测到异常登录时间，建议确认操作");
            if (list.Count == 0) list.Add("登录安全性良好");
            return list;
        }
    }
}
